// Copyright(C) 2025 InfiniFlow, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

module;

#include "base_statement.h"
#include "parser.h"
#include "unit_test/gtest_expand.h"

module infinity_core:ut.logical_compact;

import :ut.base_test;
import :ut.sql_runner;
import :infinity_exception;
import third_party;
import :logger;
import :infinity_context;
import :ut.request_test;
import :status;
import :txn_state;
import :new_txn_manager;
import :new_txn;
import :data_block;
import :column_vector;
import :table_def;
import :config;
import :query_result;
import :query_context;
import :logical_node;
import :logical_planner;

import global_resource_usage;

using namespace infinity;
class LogicalCompactTest : public NewRequestTest {
public:
    std::shared_ptr<std::string> db_name;
    std::shared_ptr<ColumnDef> column_def1;
    std::shared_ptr<ColumnDef> column_def2;
    std::shared_ptr<std::string> table_name;
    std::shared_ptr<TableDef> table_def;

    void CheckLogicalNode(const std::shared_ptr<LogicalNode> &node, LogicalNodeType type) {
        if (!node) {
            return;
        }

        if (node->operator_type() == type) {
            i64 space = 4;
            LOG_INFO(fmt::format("ToString: {}", node->ToString(space)));

            [[maybe_unused]] auto column_bindings = node->GetColumnBindings();
            [[maybe_unused]] auto output_names = node->GetOutputNames();
            [[maybe_unused]] auto output_types = node->GetOutputTypes();
        }

        CheckLogicalNode(node->left_node(), type);
        CheckLogicalNode(node->right_node(), type);
    }
};

INSTANTIATE_TEST_SUITE_P(TestWithDifferentParams, LogicalCompactTest, ::testing::Values(BaseTestParamStr::NEW_CONFIG_PATH));

TEST_P(LogicalCompactTest, test1) {
    // Create table
    {
        std::string create_index_sql = "create table tb(col1 int, col2 int)";
        std::unique_ptr<QueryContext> query_context = MakeQueryContext();
        QueryResult query_result = query_context->Query(create_index_sql);
        bool ok = HandleQueryResult(query_result);
        EXPECT_TRUE(ok);
    }

    // Create index
    {
        std::string create_index_sql = "create index idx on tb(col1)";
        std::unique_ptr<QueryContext> query_context = MakeQueryContext();
        QueryResult query_result = query_context->Query(create_index_sql);
        bool ok = HandleQueryResult(query_result);
        EXPECT_TRUE(ok);
    }

    for (size_t i = 0; i < 200; i++) {
        std::string sql = fmt::format("insert into tb values({}, 'abc')", i);
        std::unique_ptr<QueryContext> query_context = MakeQueryContext();
        QueryResult query_result = query_context->Query(sql);
        bool ok = HandleQueryResult(query_result);
        EXPECT_TRUE(ok);
    }

    {
        std::string create_index_sql = "compact table tb";
        std::unique_ptr<QueryContext> query_context = MakeQueryContext();
        QueryResult query_result = query_context->Query(create_index_sql);

        auto nodes = query_context->logical_planner()->LogicalPlans();
        for (const auto &node : nodes) {
            CheckLogicalNode(node, LogicalNodeType::kCompact);
            CheckLogicalNode(node, LogicalNodeType::kCompactIndex);
        }
        bool ok = HandleQueryResult(query_result);
        EXPECT_TRUE(ok);
    }
}